﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using AliyunOpenSearch4Net.Base;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

namespace AliyunOpenSearch4Net.Data
{
    class DataPushRequest : RequestBase<Response>
    {
        public DataPushRequest()
            : base("push")
        {

        }

        public string TableName { get; set; }

        public IList<DataItem> Items { get; set; }

        protected override void RegisterQueryItems(IList<KeyValuePair<string, string>> list)
        {
            list.Add("table_name", TableName);
        }

        protected override void RegisterAdditionalItems(IList<KeyValuePair<string, string>> list)
        {
            list.Add("items", JsonConvert.SerializeObject(Items));
        }
    }

    /// <summary>
    /// Data push item
    /// </summary>
    public class DataItem
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="DataItem"/> class.
        /// </summary>
        /// <param name="cmd">The command.</param>
        public DataItem(DataCommand cmd)
            : this()
        {
            Cmd = cmd;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="DataItem"/> class.
        /// </summary>
        public DataItem()
        {
            Fields = new FieldCollection();
            Timestamp = DateTime.Now;
        }

        /// <summary>
        /// Gets or sets the command.
        /// </summary>
        /// <value>
        /// The command.
        /// </value>
        [JsonProperty("cmd")]
        [JsonConverter(typeof(StringEnumConverter))]
        public DataCommand Cmd { get; set; }

        /// <summary>
        /// Gets or sets the timestamp.
        /// </summary>
        /// <value>
        /// The timestamp.
        /// </value>
        [JsonProperty("timestamp")]        
        [JsonConverter(typeof(UnixTicksDateTimeConverter))]
        public DateTime Timestamp { get; set; }

        /// <summary>
        /// Gets or sets the fields.
        /// </summary>
        /// <value>
        /// The fields.
        /// </value>
        [JsonProperty("fields")]
        public FieldCollection Fields { get; set; }
    }

    /// <summary>
    /// Data operation command
    /// </summary>
    public enum DataCommand
    {
        /// <summary>
        /// add new record
        /// </summary>
        add,
        /// <summary>
        /// update existing record
        /// </summary>
        update,
        /// <summary>
        /// delete existing record
        /// </summary>
        delete
    }

    /// <summary>
    /// name value pairs collection
    /// </summary>
    [JsonConverter(typeof(FieldCollectionConverter))]
    public class FieldCollection : List<INamedItem>
    {
        /// <summary>
        /// Add one field with name and value
        /// </summary>
        /// <typeparam name="TItemValue">The type of the item value.</typeparam>
        /// <param name="name">The name.</param>
        /// <param name="item">The item value.</param>
        /// <returns></returns>
        public FieldCollection Add<TItemValue>(string name, TItemValue item)
        {
            Add(new NamedItem<TItemValue>(name, item));
            return this;
        }
    }

    class FieldCollectionConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return typeof(FieldCollection).IsAssignableFrom(objectType);
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            var fields = value as FieldCollection;

            writer.WriteStartObject();

            foreach(var item in fields)
            {
                writer.WritePropertyName(item.Name);
                serializer.Serialize(writer, item.Item);
            }

            writer.WriteEndObject();
        }
    }
}
